#include "VariablesMath.c"

typedef struct
{
    token* Lexems;
    long Length;
    long Position;
    MERROR error;
    int Marks;
} code;

FILE* dest;

int GetIO(code* Source);
int GetOp(code* Source);
int GetAssign(code* Source);
int GetOps(code* Source);
int GetGroupOps(code* Source);
code* code_ctor(char* FileName);
int GetCond(code* Source);
int GetFor(code* Source);
int GetWhile(code* Source);
int GetCycle(code* Source);

int main()
{
    code* source = code_ctor("source.txt");
    if (!source)
    {
        puts("No such file");
        return 0;
    }
    dest = fopen("bin.txt", "w");
    if (!dest) return 0;
    GetCycle(source);
    printf("%d", source -> error);
    fclose(dest);
    return 0;
}

int GetAssign(code* Source)
{
    if (Source -> Lexems[Source -> Position].type != VAR)
    {
        Source -> error = NOVAR;
        return 0;
    }
    char var = Source -> Lexems[Source -> Position].var;
    if (!('a' <= var && var <= 'd'))
    {
        Source -> error = UNKNOWNVAR;
        return 0;
    }
    Source -> Position++;
    if (Source -> Lexems[Source -> Position].type != OP ||
        Source -> Lexems[Source -> Position].op != '=')
    {
        Source -> error = NOASSIGN;
        return 0;
    }
    Source -> Position++;
    expression* Exp = exp_ctor(&(Source -> Lexems[Source -> Position]));
    if (!Exp)
    {
        Source -> error = NOEXP;
        return 0;
    }
    tree* ExpTree = GetG(Exp);
    if (!ExpTree)
    {
        Source -> error = Exp -> error;
        return 0;
    }
    Source -> Position += Exp -> length;
    if (Source -> Lexems[Source -> Position].type != EOE)
    {
        Source -> error = NOEOE;
        return 0;
    }
    Source -> Position++;

    exp_dtor(Exp);
    MathDump(ExpTree, dest);
    tree_dtor(ExpTree);
    fprintf(dest, "pop %cx\n", var);
    return 1;
}

int GetOps(code* Source)
{
    while(Source -> Lexems[Source -> Position].type == VAR || (Source -> Lexems[Source -> Position].type == BR && Source -> Lexems[Source -> Position].br == '{')
          || Source -> Lexems[Source -> Position].type == IO || Source -> Lexems[Source -> Position].type == CONDOP)
    {
        if (Source -> Lexems[Source -> Position].type == VAR || Source -> Lexems[Source -> Position].type == IO)
        {
            GetOp(Source);
            if (Source -> error) return 0;
        }
        else
        {
            GetCycle(Source);
            if (Source -> error) return 0;
        }
    }
    return 1;
}

int GetGroupOps(code* Source)
{
    if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != '{')
    {
        Source -> error = NOBKT;
        return 0;
    }
    Source -> Position++;
    GetOps(Source);
    if (Source -> error) return 0;
    if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != '}')
    {
        Source -> error = NOBKT;
        return 0;
    }
    Source -> Position++;
    return 1;
}


int GetOp(code* Source)
{
    if (Source -> Lexems[Source -> Position].type == VAR) return GetAssign(Source);
    else return GetIO(Source);
}

int GetIO(code* Source)
{
    if (Source -> Lexems[Source -> Position].type != IO)
    {
        Source -> error = NOIO;
        return 0;
    }
    if (Source -> Lexems[Source -> Position].io == SCAN)
    {
        fprintf(dest, "scan\n");
        Source -> Position++;
        if (Source -> Lexems[Source -> Position].type != VAR)
        {
            Source -> error = NOVAR;
            return 0;
        }
        if (!('a' <= Source -> Lexems[Source -> Position].var && Source -> Lexems[Source -> Position].var <= 'd'))
        {
            Source -> error = UNKNOWNVAR;
            return 0;
        }

        fprintf(dest, "pop %cx\n", Source -> Lexems[Source -> Position].var);
        Source -> Position++;
        if (Source -> Lexems[Source -> Position].type != EOE)
        {
            Source -> error = NOEOE;
            return 0;
        }
        Source -> Position++;
        return 1;
    }
    else if(Source -> Lexems[Source -> Position].io == PRINT)
    {
        Source -> Position++;
        /*if (Source -> Lexems[Source -> Position].type != VAR)
        {
            Source -> error = NOVAR;
            return 0;
        }
        if (!('a' <= Source -> Lexems[Source -> Position].var && Source -> Lexems[Source -> Position].var <= 'd'))
        {
            Source -> error = UNKNOWNVAR;
            return 0;
        }
        fprintf(dest, "push %cx\n", Source -> Lexems[Source -> Position].var);
        Source -> Position++;*/

        expression* Exp = exp_ctor(&(Source -> Lexems[Source -> Position]));
        if (!Exp)
        {
            Source -> error = NOEXP;
            return 0;
        }
        tree* ExpTree = GetG(Exp);
        if (!ExpTree)
        {
            Source -> error = Exp -> error;
            return 0;
        }
        Source -> Position += Exp -> length;
        if (Source -> Lexems[Source -> Position].type != EOE)
        {
            Source -> error = NOEOE;
            return 0;
        }
        Source -> Position++;

        exp_dtor(Exp);
        MathDump(ExpTree, dest);
        tree_dtor(ExpTree);

        fprintf(dest, "print\n");
        return 1;
    }
    else
    {
        Source -> error = WRONGIO;
        return 0;
    }

}

code* code_ctor(char* FileName)
{
    if (!FileName) return NULL;
    char* Text = FileToArray(FileName);
    if (!Text) return NULL;
    code* result = (code*) calloc(1, sizeof(code));
    if (!result) return NULL;
    result -> Lexems = LexAn(Text);
    if (!result -> Lexems) return NULL;
    result -> Length = 0;
    while(result -> Lexems[result -> Length].type != EOC)
    {
        result -> Length++;
    }
    result -> Position = 0;
    result -> error = NOERROR;
    result -> Marks = 1;
    free(Text);
    return result;
}


int GetCond(code* Source)
{
    if(Source -> Lexems[Source -> Position].type == CONDOP && Source -> Lexems[Source -> Position].condop == IF)
    {
        Source -> Position++;
        if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != '[')
        {
            Source -> error = NOBKT;
            return 0;
        }
        Source -> Position++;
        expression* Exp1 = exp_ctor(&(Source -> Lexems[Source -> Position]));
        if (!Exp1)
        {
            Source -> error = NOEXP;
            return 0;
        }
        tree* Exp1Tree = GetG(Exp1);
        if (!Exp1Tree)
        {
            Source -> error = Exp1 -> error;
            return 0;
        }
        Source -> Position += Exp1 -> length;
        exp_dtor(Exp1);
        MathDump(Exp1Tree, dest);
        tree_dtor(Exp1Tree);
        if (Source -> Lexems[Source -> Position].type != COND)
        {
            Source -> error = NOCOND;
            return 0;
        }
        CONDITION cond = Source -> Lexems[Source -> Position].cond;
        Source -> Position++;

        expression* Exp2 = exp_ctor(&(Source -> Lexems[Source -> Position]));
        if (!Exp2)
        {
            Source -> error = NOEXP;
            return 0;
        }
        tree* Exp2Tree = GetG(Exp2);
        if (!Exp2Tree)
        {
            Source -> error = Exp2 -> error;
            return 0;
        }
        Source -> Position += Exp2 -> length;
        exp_dtor(Exp2);
        MathDump(Exp2Tree, dest);
        tree_dtor(Exp2Tree);
        if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != ']')
        {
            Source -> error = NOBKT;
            return 0;
        }
        Source -> Position++;
        if (Source -> Marks > 7)
        {
            Source -> error = NOMARKS;
            return 0;
        }
        /*switch (cond)
        {
            case JA:
            {
                fprintf(dest, "jbe :%d\n", Source -> Marks);
                break;
            }
            case JAE:
            {
                fprintf(dest, "jb :%d\n", Source -> Marks);
                break;
            }
            case JB:
            {
                fprintf(dest, "jae :%d\n", Source -> Marks);
                break;
            }
            case JBE:
            {
                fprintf(dest, "ja :%d\n", Source -> Marks);
                break;
            }
            case JE:
            {
                fprintf(dest, "jne :%d\n", Source -> Marks);
                break;
            }
            case JNE:
            {
                fprintf(dest, "je :%d\n", Source -> Marks);
                break;
            }
            default:
            {
                Source -> error = WRONGCOND;
                return 0;
            }
        }
        Source -> Marks++;
        GetGroupOps(Source);
        if (Source -> error) return 0;
        int iselse = 0;
        if (Source -> Lexems[Source -> Position].type == CONDOP && Source -> Lexems[Source -> Position].condop == ELSE)
        {
            iselse = 1;
            if (Source -> Marks > 10)
            {
                Source -> error = NOMARKS;
                return 0;
            }
            fprintf(dest, "jump :%d\n", Source -> Marks);
            Source -> Position++;
            Source -> Marks++;
        }
        fprintf(dest, ":%d\n", Source -> Marks - 2);
        if (iselse)
        {
            GetGroupOps(Source);
            if (Source -> error) return 0;
            fprintf(dest, ":%d\n", Source -> Marks - 1);
        }
        return 1;
    }*/
        int FirstMark = Source -> Marks;
        Source -> Marks += 3;
        switch (cond)
            {
                case JA:
                {
                    fprintf(dest, "ja :%d\njbe :%d\n", FirstMark, FirstMark + 1);
                    break;
                }
                case JAE:
                {
                    fprintf(dest, "jae :%d\njb :%d\n", FirstMark, FirstMark + 1);
                    break;
                }
                case JB:
                {
                    fprintf(dest, "jb :%d\njae :%d\n", FirstMark, FirstMark + 1);
                    break;
                }
                case JBE:
                {
                    fprintf(dest, "jbe :%d\nja :%d\n", FirstMark, FirstMark + 1);
                    break;
                }
                case JE:
                {
                    fprintf(dest, "je :%d\njne :%d\n", FirstMark, FirstMark + 1);
                    break;
                }
                case JNE:
                {
                    fprintf(dest, "jne :%d\nje :%d\n", FirstMark, FirstMark + 1);
                    break;
                }
                default:
                {
                    Source -> error = WRONGCOND;
                    return 0;
                }
            }
        fprintf(dest, ":%d\n", FirstMark);
        GetGroupOps(Source);
        if (Source -> error) return 0;
        int iselse = 0;
        if (Source -> Lexems[Source -> Position].type == CONDOP && Source -> Lexems[Source -> Position].condop == ELSE)
        {
            iselse = 1;
            fprintf(dest, "jump :%d\n", FirstMark + 2);
        }
        fprintf(dest, ":%d\n", FirstMark + 1);
        if (iselse)
        {
            Source -> Position++;
            GetGroupOps(Source);
            if (Source -> error) return 0;
            fprintf(dest, ":%d\n", FirstMark + 2);
        }
        return 0;
    }
    if ((Source -> Lexems[Source -> Position].type == BR && Source -> Lexems[Source -> Position].br == '{') ||
        Source -> Lexems[Source -> Position].type == VAR || Source -> Lexems[Source -> Position].type == IO)
    {
        return GetGroupOps(Source);
    }
    else
    {
        Source -> error = WRONGTEXT;
        return 0;
    }
}

int GetFor(code* Source)
{
    if (Source -> Lexems[Source -> Position].type != CYCLE || Source -> Lexems[Source -> Position].cycle != FOR)
    {
        Source -> error = NOERROR;
        return 0;
    }
    Source -> Position++;
    if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != '[')
    {
        Source -> error = NOBKT;
        return 0;
    }
    Source -> Position++;
    GetAssign(Source);
    if (Source -> error) return 0;

    expression* Exp1 = exp_ctor(&(Source -> Lexems[Source -> Position]));
    if (!Exp1)
    {
        Source -> error = NOEXP;
        return 0;
    }
    tree* Exp1Tree = GetG(Exp1);
    if (!Exp1Tree)
    {
        Source -> error = Exp1 -> error;
        return 0;
    }
    Source -> Position += Exp1 -> length;
    exp_dtor(Exp1);

    if (Source -> Lexems[Source -> Position].type != COND)
    {
        Source -> error = NOCOND;
        return 0;
    }
    CONDITION cond = Source -> Lexems[Source -> Position].cond;
    Source -> Position++;
    expression* Exp2 = exp_ctor(&(Source -> Lexems[Source -> Position]));
    if (!Exp2)
    {
        Source -> error = NOEXP;
        return 0;
    }
    tree* Exp2Tree = GetG(Exp2);
    if (!Exp2Tree)
    {
        Source -> error = Exp2 -> error;
        return 0;
    }
    Source -> Position += Exp2 -> length;
    exp_dtor(Exp2);
    if (Source -> Lexems[Source -> Position].type != EOE)
    {
        Source -> error = NOEOE;
        return 0;
    }
    Source -> Position++;

    if (Source -> Lexems[Source -> Position].type != VAR)
    {
        Source -> error = NOVAR;
        return 0;
    }
    char var = Source -> Lexems[Source -> Position].var;
    if (!('a' <= var && var <= 'd'))
    {
        Source -> error = UNKNOWNVAR;
        return 0;
    }
    Source -> Position++;
    if (Source -> Lexems[Source -> Position].type != OP ||
        Source -> Lexems[Source -> Position].op != '=')
    {
        Source -> error = NOASSIGN;
        return 0;
    }
    Source -> Position++;

    expression* Exp3 = exp_ctor(&(Source -> Lexems[Source -> Position]));
    if (!Exp3)
    {
        Source -> error = NOEXP;
        return 0;
    }
    tree* Exp3Tree = GetG(Exp3);
    if (!Exp3Tree)
    {
        Source -> error = Exp3 -> error;
        return 0;
    }
    Source -> Position += Exp3 -> length;
    exp_dtor(Exp3);
    if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != ']')
    {
        Source -> error = NOBKT;
        return 0;
    }
    Source -> Position++;
    if (Source -> Marks > 8)
    {
        Source -> error = NOMARKS;
        return 0;
    }
    int FirstMark = Source -> Marks;
    Source -> Marks += 2;
    fprintf(dest, ":%d\n", FirstMark + 1);
    MathDump(Exp1Tree, dest);
    MathDump(Exp2Tree, dest);
    switch (cond)
        {
            case JA:
            {
                fprintf(dest, "jbe :%d\n", FirstMark);
                break;
            }
            case JAE:
            {
                fprintf(dest, "jb :%d\n", FirstMark);
                break;
            }
            case JB:
            {
                fprintf(dest, "jae :%d\n", FirstMark);
                break;
            }
            case JBE:
            {
                fprintf(dest, "ja :%d\n", FirstMark);
                break;
            }
            case JE:
            {
                fprintf(dest, "jne :%d\n", FirstMark);
                break;
            }
            case JNE:
            {
                fprintf(dest, "je :%d\n", FirstMark);
                break;
            }
            default:
            {
                Source -> error = WRONGCOND;
                return 0;
            }
        }
    GetCycle(Source);
    if (Source -> error) return 0;
    MathDump(Exp3Tree, dest);
    fprintf(dest, "pop %cx\n", var);
    fprintf(dest, "jump :%d\n", FirstMark + 1);
    fprintf(dest, ":%d\n", FirstMark);
    tree_dtor(Exp1Tree);
    tree_dtor(Exp2Tree);
    tree_dtor(Exp3Tree);
    return 1;
}

int GetWhile(code* Source)
{
    if (Source -> Lexems[Source -> Position].type != CYCLE || Source -> Lexems[Source -> Position].cycle != WHILE)
    {
        Source -> error = NOWHILE;
        return 0;
    }
    Source -> Position++;
    if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != '[')
    {
        Source -> error = NOBKT;
        return 0;
    }
    Source -> Position++;

    expression* Exp1 = exp_ctor(&(Source -> Lexems[Source -> Position]));
    if (!Exp1)
    {
        Source -> error = NOEXP;
        return 0;
    }
    tree* Exp1Tree = GetG(Exp1);
    if (!Exp1Tree)
    {
        Source -> error = Exp1 -> error;
        return 0;
    }
    Source -> Position += Exp1 -> length;
    exp_dtor(Exp1);

    if (Source -> Lexems[Source -> Position].type != COND)
    {
        Source -> error = NOCOND;
        return 0;
    }
    CONDITION cond = Source -> Lexems[Source -> Position].cond;
    Source -> Position++;
    expression* Exp2 = exp_ctor(&(Source -> Lexems[Source -> Position]));
    if (!Exp2)
    {
        Source -> error = NOEXP;
        return 0;
    }
    tree* Exp2Tree = GetG(Exp2);
    if (!Exp2Tree)
    {
        Source -> error = Exp2 -> error;
        return 0;
    }
    Source -> Position += Exp2 -> length;
    exp_dtor(Exp2);
    if (Source -> Lexems[Source -> Position].type != BR || Source -> Lexems[Source -> Position].br != ']')
    {
        Source -> error = NOBKT;
        return 0;
    }
    Source -> Position++;
    int FirstMark = Source -> Marks;
    Source -> Marks += 2;
    fprintf(dest, ":%d\n", FirstMark + 1);
    MathDump(Exp1Tree, dest);
    MathDump(Exp2Tree, dest);
    switch (cond)
        {
            case JA:
            {
                fprintf(dest, "jbe :%d\n", FirstMark);
                break;
            }
            case JAE:
            {
                fprintf(dest, "jb :%d\n", FirstMark);
                break;
            }
            case JB:
            {
                fprintf(dest, "jae :%d\n", FirstMark);
                break;
            }
            case JBE:
            {
                fprintf(dest, "ja :%d\n", FirstMark);
                break;
            }
            case JE:
            {
                fprintf(dest, "jne :%d\n", FirstMark);
                break;
            }
            case JNE:
            {
                fprintf(dest, "je :%d\n", FirstMark);
                break;
            }
            default:
            {
                Source -> error = WRONGCOND;
                return 0;
            }
        }
    GetCycle(Source);
    if (Source -> error) return 0;
    fprintf(dest, ":%d\n", FirstMark);
    return 1;
}

int GetCycle(code* Source)
{
    if (Source -> Lexems[Source -> Position].type == CYCLE)
    {
        if (Source -> Lexems[Source -> Position].cycle == WHILE)
        {
            return GetWhile(Source);
            if (Source -> error) return 0;
        }
        if (Source -> Lexems[Source -> Position].cycle == FOR)
        {
            return GetFor(Source);
            if (Source -> error) return 0;
        }
        else
        {
            Source -> error = WRONGCYCLE;
            return 0;
        }
    }
    if (Source -> Lexems[Source -> Position].type == IO ||
        (Source -> Lexems[Source -> Position].type == BR && Source -> Lexems[Source -> Position].br == '{') ||
        Source -> Lexems[Source -> Position].type == VAR ||
        (Source -> Lexems[Source -> Position].type == CONDOP && Source -> Lexems[Source -> Position].condop == IF))
    {
        return GetCond(Source);
    }
    else
    {
        Source -> error = WRONGLEXEMS;
        return 0;
    }
}
